<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>flask 源码解析：session | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="flask 源码解析：session | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2017/03/08/flask-insight-session/">

	
	<meta property="article:published_time" content="2017-03-08T00:03:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2017/03/08/flask-insight-session/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2017-03-07T16:00:00.000Z" itemprop="datePublished">
                    2017-03-8
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">flask 源码解析：session</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <p>这是 flask 源码解析系列文章的其中一篇，本系列所有文章列表：</p>
<ul>
<li><a href="http://cizixs.com/2017/01/10/flask-insight-introduction">flask 源码解析：简介</a></li>
<li><a href="http://cizixs.com/2017/01/11/flask-insight-start-process">flask 源码解析：应用启动流程</a></li>
<li><a href="http://cizixs.com/2017/01/12/flask-insight-routing">flask 源码解析：路由</a></li>
<li><a href="http://cizixs.com/2017/01/13/flask-insight-context">flask 源码解析：上下文</a></li>
<li><a href="http://cizixs.com/2017/01/18/flask-insight-request">flask 源码解析：请求</a></li>
<li><a href="http://cizixs.com/2017/01/22/flask-insight-response">flask 源码解析：响应</a></li>
<li><a href="http://cizixs.com/2017/03/08/flask-insight-session">flask 源码解析：session</a></li>
</ul>
<h2 id="session-简介"><a href="#session-简介" class="headerlink" title="session 简介"></a>session 简介</h2><p>在解析 session 的实现之前，我们先介绍一下 session 怎么使用。session 可以看做是在不同的请求之间保存数据的方法，因为 HTTP 是无状态的协议，但是在业务应用上我们希望知道不同请求是否是同一个人发起的。比如购物网站在用户点击进入购物车的时候，服务器需要知道是哪个用户执行了这个操作。</p>
<p>在 flask 中使用 session 也很简单，只要使用  <code>from flask import session</code> 导入这个变量，在代码中就能直接通过读写它和 session 交互。</p>
<pre class=" language-bash"><code class="language-bash">from flask <span class="token function">import</span> Flask, session, escape, request

app <span class="token operator">=</span> Flask<span class="token punctuation">(</span>__name__<span class="token punctuation">)</span>
app.secret_key <span class="token operator">=</span> <span class="token string">'please-generate-a-random-secret_key'</span>


@app.route<span class="token punctuation">(</span><span class="token string">"/"</span><span class="token punctuation">)</span>
def index<span class="token punctuation">(</span><span class="token punctuation">)</span>:
    <span class="token keyword">if</span> <span class="token string">'username'</span> <span class="token keyword">in</span> session:
        <span class="token keyword">return</span> <span class="token string">'hello, {}\n'</span>.format<span class="token punctuation">(</span>escape<span class="token punctuation">(</span>session<span class="token punctuation">[</span><span class="token string">'username'</span><span class="token punctuation">]</span><span class="token punctuation">))</span>
    <span class="token keyword">return</span> <span class="token string">'hello, stranger\n'</span>


@app.route<span class="token punctuation">(</span><span class="token string">"/login"</span>, methods<span class="token operator">=</span><span class="token punctuation">[</span><span class="token string">'POST'</span><span class="token punctuation">]</span><span class="token punctuation">)</span>
def login<span class="token punctuation">(</span><span class="token punctuation">)</span>:
    session<span class="token punctuation">[</span><span class="token string">'username'</span><span class="token punctuation">]</span> <span class="token operator">=</span> request.form<span class="token punctuation">[</span><span class="token string">'username'</span><span class="token punctuation">]</span>
    <span class="token keyword">return</span> <span class="token string">'login success'</span>


<span class="token keyword">if</span> __name__ <span class="token operator">==</span> <span class="token string">'__main__'</span><span class="token keyword">:</span>
    app.run<span class="token punctuation">(</span>host<span class="token operator">=</span><span class="token string">'0.0.0.0'</span>, port<span class="token operator">=</span>5000, debug<span class="token operator">=</span>True<span class="token punctuation">)</span>
</code></pre>
<p>上面这段代码模拟了一个非常简单的登陆逻辑，用户访问 <code>POST /login</code> 来登陆，后面访问页面的时候 <code>GET /</code>，会返回该用户的名字。我们看一下具体的操作实例（下面的操作都是用 <a href="https://github.com/jkbrzt/httpie" target="_blank" rel="noopener">httpie</a> 来执行的，使用 <code>curl</code> 命令也能达到相同的效果）：</p>
<p>直接访问的话，我们可以看到返回 <code>hello stranger</code>：</p>
<pre class=" language-bash"><code class="language-bash">➜  ~ http -v http://127.0.0.1:5000/
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Host: 127.0.0.1:5000
User-Agent: HTTPie/0.8.0


HTTP/1.0 200 OK
Content-Length: 14
Content-Type: text/html<span class="token punctuation">;</span> charset<span class="token operator">=</span>utf-8
Date: Wed, 01 Mar 2017 04:22:18 GMT
Server: Werkzeug/0.11.2 Python/2.7.10

hello stranger
</code></pre>
<p>然后我们模拟登陆请求，<code>-v</code> 是打印出请求，<code>-f</code> 是告诉服务器这是表单数据，<code>--session=mysession</code> 是把请求的 cookie 等信息保存到这个变量中，后面可以通过变量来指定 session：</p>
<pre class=" language-bash"><code class="language-bash">➜  ~ http -v -f --session<span class="token operator">=</span>mysession POST http://127.0.0.1:5000/login username<span class="token operator">=</span>cizixs
POST /login HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Content-Length: 15
Content-Type: application/x-www-form-urlencoded<span class="token punctuation">;</span> charset<span class="token operator">=</span>utf-8
Host: 127.0.0.1:5000
User-Agent: HTTPie/0.8.0

username<span class="token operator">=</span>cizixs

HTTP/1.0 200 OK
Content-Length: 13
Content-Type: text/html<span class="token punctuation">;</span> charset<span class="token operator">=</span>utf-8
Date: Wed, 01 Mar 2017 04:20:54 GMT
Server: Werkzeug/0.11.2 Python/2.7.10
Set-Cookie: session<span class="token operator">=</span>eyJ1c2VybmFtZSI6ImNpeml4cyJ9.C5fdpg.fqm3FTv0kYE2TuOyGF1mx2RuYQ4<span class="token punctuation">;</span> HttpOnly<span class="token punctuation">;</span> Path<span class="token operator">=</span>/

login success
</code></pre>
<p>最重要的是我们看到 response 中有 <code>Set-Cookie</code> 的头部，cookie 的键是 <code>session</code>，值是一堆看起来随机的字符串。</p>
<p>继续，这个时候我们用 <code>--session=mysession</code> 参数把这次的请求带上保存在 <code>mysession</code> 中的信息，登陆后访问，可以看到登陆的用户名：</p>
<pre class=" language-bash"><code class="language-bash">➜  ~ http -v --session<span class="token operator">=</span>mysession http://127.0.0.1:5000/
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Cookie: session<span class="token operator">=</span>eyJ1c2VybmFtZSI6ImNpeml4cyJ9.C5fevg.LE03yEZDWTUMQW-nNkTr1zBEhKk
Host: 127.0.0.1:5000
User-Agent: HTTPie/0.8.0


HTTP/1.0 200 OK
Content-Length: 11
Content-Type: text/html<span class="token punctuation">;</span> charset<span class="token operator">=</span>utf-8
Date: Wed, 01 Mar 2017 04:25:46 GMT
Server: Werkzeug/0.11.2 Python/2.7.10
Set-Cookie: session<span class="token operator">=</span>eyJ1c2VybmFtZSI6ImNpeml4cyJ9.C5feyg.sfFCDIqfef4i8cvxUClUUGQNcHA<span class="token punctuation">;</span> HttpOnly<span class="token punctuation">;</span> Path<span class="token operator">=</span>/

hellocizixs
</code></pre>
<p>这次注意在发送的请求中，客户端带了 <code>Cookie</code> 头部，上面的值保存了前一个请求的 response 给我们设置的值。</p>
<p>总结一下：session 是通过在客户端设置 cookie 实现的，每次客户端发送请求的时候会附带着所有的 cookie，而里面保存着一些重要的信息（比如这里的用户信息），这样服务器端就能知道客户端的信息，然后根据这些数据做出对应的判断，就好像不同请求之间是有记忆的。</p>
<h2 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h2><p>我们知道 session 是怎么回事了，这部分就分析一下 flask 是怎么实现它的。</p>
<h3 id="请求过程"><a href="#请求过程" class="headerlink" title="请求过程"></a>请求过程</h3><p>不难想象，session 的大致解析过程是这样的：</p>
<ul>
<li>请求过来的时候，flask 会根据 cookie 信息创建出 session 变量（如果 cookie 不存在，这个变量有可能为空），保存在该请求的上下文中</li>
<li>视图函数可以获取 session 中的信息，实现自己的逻辑处理</li>
<li>flask 会在发送 response 的时候，根据 session 的值，把它写回到 cookie 中</li>
</ul>
<p>注意：session 和 cookie 的转化过程中，应该考虑到安全性，不然直接使用伪造的 cookie 会是个很大的安全隐患。</p>
<p>在<a href="http://cizixs.com/2017/01/13/flask-insight-context"> flask 上下文那篇文章</a>中，我们知道，每次请求过来的时候，我们访问的 <code>request</code> 和 <code>session</code> 变量都是 <code>RequestContext</code> 实例的变量。在 <code>RequestContext.Push()</code> 方法的最后有这么一段代码：</p>
<pre class=" language-python"><code class="language-python">self<span class="token punctuation">.</span>session <span class="token operator">=</span> self<span class="token punctuation">.</span>app<span class="token punctuation">.</span>open_session<span class="token punctuation">(</span>self<span class="token punctuation">.</span>request<span class="token punctuation">)</span>
<span class="token keyword">if</span> self<span class="token punctuation">.</span>session <span class="token keyword">is</span> None<span class="token punctuation">:</span>
    self<span class="token punctuation">.</span>session <span class="token operator">=</span> self<span class="token punctuation">.</span>app<span class="token punctuation">.</span>make_null_session<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p>它初始化了 <code>session</code> 变量，保存在 <code>RequestContext</code> 上，这样后面就能直接通过 <code>from flask import session</code> 来使用它。如果没有设置 <code>secret_key</code> 变量， <code>open_session</code> 就会返回 None，这个时候会调用 <code>make_null_session</code> 来生成一个空的 session，这个特殊的 session 不能进行任何读写操作，不然会报异常给用户。</p>
<p>我们来看看 <code>open_session</code> 方法：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">open_session</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> request<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> self<span class="token punctuation">.</span>session_interface<span class="token punctuation">.</span>open_session<span class="token punctuation">(</span>self<span class="token punctuation">,</span> request<span class="token punctuation">)</span>
</code></pre>
<p>在 <code>Flask</code> 中，所有和 session 有关的调用，都是转发到 <code>self.session_interface</code> 的方法调用上（这样用户就能用自定义的 <code>session_interface</code> 来控制 session 的使用）。而默认的 <code>session_inerface</code> 有默认值：</p>
<pre class=" language-python"><code class="language-python">session_interface <span class="token operator">=</span> SecureCookieSessionInterface<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p>后面遇到 session 有关方法解释，我们会直接讲解 <code>SecureCookieSessionInterface</code> 的代码实现，跳过中间的这个转发说明。</p>
<pre class=" language-python"><code class="language-python">null_session_class <span class="token operator">=</span> NullSession

<span class="token keyword">def</span> <span class="token function">make_null_session</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> app<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">return</span> self<span class="token punctuation">.</span>null_session_class<span class="token punctuation">(</span><span class="token punctuation">)</span>

<span class="token keyword">def</span> <span class="token function">open_session</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> app<span class="token punctuation">,</span> request<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token comment" spellcheck="true"># 获取 session 签名的算法</span>
    s <span class="token operator">=</span> self<span class="token punctuation">.</span>get_signing_serializer<span class="token punctuation">(</span>app<span class="token punctuation">)</span>
    <span class="token keyword">if</span> s <span class="token keyword">is</span> None<span class="token punctuation">:</span>
        <span class="token keyword">return</span> None

    <span class="token comment" spellcheck="true"># 从 cookie 中获取 session 变量的值</span>
    val <span class="token operator">=</span> request<span class="token punctuation">.</span>cookies<span class="token punctuation">.</span>get<span class="token punctuation">(</span>app<span class="token punctuation">.</span>session_cookie_name<span class="token punctuation">)</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> val<span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>session_class<span class="token punctuation">(</span><span class="token punctuation">)</span>

    <span class="token comment" spellcheck="true"># 因为 cookie 的数据需要验证是否有篡改，所以需要签名算法来读取里面的值</span>
    max_age <span class="token operator">=</span> total_seconds<span class="token punctuation">(</span>app<span class="token punctuation">.</span>permanent_session_lifetime<span class="token punctuation">)</span>
    <span class="token keyword">try</span><span class="token punctuation">:</span>
        data <span class="token operator">=</span> s<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>val<span class="token punctuation">,</span> max_age<span class="token operator">=</span>max_age<span class="token punctuation">)</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>session_class<span class="token punctuation">(</span>data<span class="token punctuation">)</span>
    <span class="token keyword">except</span> BadSignature<span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>session_class<span class="token punctuation">(</span><span class="token punctuation">)</span>
</code></pre>
<p><code>open_session</code> 根据请求中的 cookie 来获取对应的 session 对象。之所以有 <code>app</code> 参数，是因为根据 app 中的安全设置（比如签名算法、secret_key）对 cookie 进行验证。</p>
<p>这里有两点需要特殊说明的：签名算法是怎么工作的？session 对象到底是怎么定义的？</p>
<h3 id="session-对象"><a href="#session-对象" class="headerlink" title="session 对象"></a>session 对象</h3><p>默认的 session 对象是 <code>SecureCookieSession</code>，这个类就是一个基本的字典，外加一些特殊的属性，比如 <code>permanent</code>（flask 插件会用到这个变量）、<code>modified</code>（表明实例是否被更新过，如果更新过就要重新计算并设置 cookie，因为计算过程比较贵，所以如果对象没有被修改，就直接跳过）。</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">class</span> <span class="token class-name">SessionMixin</span><span class="token punctuation">(</span>object<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token keyword">def</span> <span class="token function">_get_permanent</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">return</span> self<span class="token punctuation">.</span>get<span class="token punctuation">(</span><span class="token string">'_permanent'</span><span class="token punctuation">,</span> <span class="token boolean">False</span><span class="token punctuation">)</span>

    <span class="token keyword">def</span> <span class="token function">_set_permanent</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> value<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">[</span><span class="token string">'_permanent'</span><span class="token punctuation">]</span> <span class="token operator">=</span> bool<span class="token punctuation">(</span>value<span class="token punctuation">)</span>

    <span class="token comment" spellcheck="true">#: this reflects the ``'_permanent'`` key in the dict.</span>
    permanent <span class="token operator">=</span> property<span class="token punctuation">(</span>_get_permanent<span class="token punctuation">,</span> _set_permanent<span class="token punctuation">)</span>
    <span class="token keyword">del</span> _get_permanent<span class="token punctuation">,</span> _set_permanent

    modified <span class="token operator">=</span> <span class="token boolean">True</span>

<span class="token keyword">class</span> <span class="token class-name">SecureCookieSession</span><span class="token punctuation">(</span>CallbackDict<span class="token punctuation">,</span> SessionMixin<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token triple-quoted-string string">"""Base class for sessions based on signed cookies."""</span>

    <span class="token keyword">def</span> <span class="token function">__init__</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> initial<span class="token operator">=</span>None<span class="token punctuation">)</span><span class="token punctuation">:</span>
        <span class="token keyword">def</span> <span class="token function">on_update</span><span class="token punctuation">(</span>self<span class="token punctuation">)</span><span class="token punctuation">:</span>
            self<span class="token punctuation">.</span>modified <span class="token operator">=</span> <span class="token boolean">True</span>
        CallbackDict<span class="token punctuation">.</span>__init__<span class="token punctuation">(</span>self<span class="token punctuation">,</span> initial<span class="token punctuation">,</span> on_update<span class="token punctuation">)</span>
        self<span class="token punctuation">.</span>modified <span class="token operator">=</span> <span class="token boolean">False</span>
</code></pre>
<p>怎么知道实例的数据被更新过呢？ <code>SecureCookieSession</code> 是基于 <code>werkzeug/datastructures:CallbackDict</code> 实现的，这个类可以指定一个函数作为 <code>on_update</code> 参数，每次有字典操作的时候（<code>__setitem__</code>、<code>__delitem__</code>、<code>clear</code>、<code>popitem</code>、<code>update</code>、<code>pop</code>、<code>setdefault</code>）会调用这个函数。</p>
<p><strong>NOTE</strong>：<code>CallbackDict</code> 的实现很巧妙，但是并不复杂，感兴趣的可以自己参考代码。主要思路就是重载字典的一些更新操作，让它们在做原来事情的同时，额外调用一下实现保存的某个函数。</p>
<p>对于开发者来说，可以把 <code>session</code> 简单地看成字典，所有的操作都是和字典一致的。</p>
<h3 id="签名算法"><a href="#签名算法" class="headerlink" title="签名算法"></a>签名算法</h3><p>都获取 cookie 数据的过程中，最核心的几句话是：</p>
<pre class=" language-python"><code class="language-python">s <span class="token operator">=</span> self<span class="token punctuation">.</span>get_signing_serializer<span class="token punctuation">(</span>app<span class="token punctuation">)</span>
val <span class="token operator">=</span> request<span class="token punctuation">.</span>cookies<span class="token punctuation">.</span>get<span class="token punctuation">(</span>app<span class="token punctuation">.</span>session_cookie_name<span class="token punctuation">)</span>
data <span class="token operator">=</span> s<span class="token punctuation">.</span>loads<span class="token punctuation">(</span>val<span class="token punctuation">,</span> max_age<span class="token operator">=</span>max_age<span class="token punctuation">)</span>

<span class="token keyword">return</span> self<span class="token punctuation">.</span>session_class<span class="token punctuation">(</span>data<span class="token punctuation">)</span>
</code></pre>
<p>其中两句都和 <code>s</code> 有关，<code>signing_serializer</code> 保证了 cookie 和 session 的转换过程中的安全问题。如果 flask 发现请求的 cookie 被篡改了，它会直接放弃使用。</p>
<p>我们继续看 <code>get_signing_serializer</code> 方法：</p>
<pre><code>def get_signing_serializer(self, app):
    if not app.secret_key:
        return None
    signer_kwargs = dict(
        key_derivation=self.key_derivation,
        digest_method=self.digest_method
    )
    return URLSafeTimedSerializer(app.secret_key,
        salt=self.salt,
        serializer=self.serializer,
        signer_kwargs=signer_kwargs)
</code></pre><p>我们看到这里需要用到很多参数：</p>
<ul>
<li><code>secret_key</code>：密钥。这个是必须的，如果没有配置 <code>secret_key</code> 就直接使用 <code>session</code> 会报错</li>
<li><code>salt</code>：为了增强安全性而设置一个 salt 字符串（可以自行搜索“安全加盐”了解对应的原理）</li>
<li><code>serializer</code>：序列算法</li>
<li><code>signer_kwargs</code>：其他参数，包括摘要/hash算法（默认是 <code>sha1</code>）和 签名算法（默认是 <code>hmac</code>）</li>
</ul>
<p><code>URLSafeTimedSerializer</code> 是 <a href="https://pythonhosted.org/itsdangerous/" target="_blank" rel="noopener"><code>itsdangerous</code></a> 库的类，主要用来进行数据验证，增加网络中数据的安全性。<code>itsdangerours</code> 提供了多种 <code>Serializer</code>，可以方便地进行类似 json 处理的数据序列化和反序列的操作。至于具体的实现，因为篇幅限制，就不解释了。</p>
<h3 id="应答过程"><a href="#应答过程" class="headerlink" title="应答过程"></a>应答过程</h3><p>flask 会在请求过来的时候自动解析 cookie 的值，把它变成 <code>session</code> 变量。开发在视图函数中可以使用它的值，也可以对它进行更新。最后再返回的 response 中，flask 也会自动把 session 写回到 cookie。我们来看看这部分是怎么实现的！</p>
<p><a href="http://cizixs.com/2017/01/22/flask-insight-response">之前的文章</a>讲解了应答的过程，其中 <code>finalize_response</code> 方法在根据视图函数的返回生成 response 对象之后，会调用 <code>process_response</code> 方法进行处理。<code>process_response</code> 方法的最后有这样两句话：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">process_response</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">:</span>
    <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span>
    <span class="token keyword">if</span> <span class="token operator">not</span> self<span class="token punctuation">.</span>session_interface<span class="token punctuation">.</span>is_null_session<span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>session<span class="token punctuation">)</span><span class="token punctuation">:</span>
        self<span class="token punctuation">.</span>save_session<span class="token punctuation">(</span>ctx<span class="token punctuation">.</span>session<span class="token punctuation">,</span> response<span class="token punctuation">)</span>
    <span class="token keyword">return</span> response
</code></pre>
<p>这里就是 session 在应答中出现的地方，思路也很简单，如果需要就调用 <code>save_sessoin</code>，把当前上下文中的 <code>session</code> 对象保存到 response 。</p>
<p><code>save_session</code> 的代码和 <code>open_session</code> 对应：</p>
<pre class=" language-python"><code class="language-python"><span class="token keyword">def</span> <span class="token function">save_session</span><span class="token punctuation">(</span>self<span class="token punctuation">,</span> app<span class="token punctuation">,</span> session<span class="token punctuation">,</span> response<span class="token punctuation">)</span><span class="token punctuation">:</span>
        domain <span class="token operator">=</span> self<span class="token punctuation">.</span>get_cookie_domain<span class="token punctuation">(</span>app<span class="token punctuation">)</span>
        path <span class="token operator">=</span> self<span class="token punctuation">.</span>get_cookie_path<span class="token punctuation">(</span>app<span class="token punctuation">)</span>

        <span class="token comment" spellcheck="true"># 如果 session 变成了空字典，flask 会直接删除对应的 cookie</span>
        <span class="token keyword">if</span> <span class="token operator">not</span> session<span class="token punctuation">:</span>
            <span class="token keyword">if</span> session<span class="token punctuation">.</span>modified<span class="token punctuation">:</span>
                response<span class="token punctuation">.</span>delete_cookie<span class="token punctuation">(</span>app<span class="token punctuation">.</span>session_cookie_name<span class="token punctuation">,</span>
                                       domain<span class="token operator">=</span>domain<span class="token punctuation">,</span> path<span class="token operator">=</span>path<span class="token punctuation">)</span>
            <span class="token keyword">return</span>

        <span class="token comment" spellcheck="true"># 是否需要设置 cookie。如果 session 发生了变化，就一定要更新 cookie，否则用户可以 `SESSION_REFRESH_EACH_REQUEST` 变量控制是否要设置 cookie</span>
        <span class="token keyword">if</span> <span class="token operator">not</span> self<span class="token punctuation">.</span>should_set_cookie<span class="token punctuation">(</span>app<span class="token punctuation">,</span> session<span class="token punctuation">)</span><span class="token punctuation">:</span>
            <span class="token keyword">return</span>

        httponly <span class="token operator">=</span> self<span class="token punctuation">.</span>get_cookie_httponly<span class="token punctuation">(</span>app<span class="token punctuation">)</span>
        secure <span class="token operator">=</span> self<span class="token punctuation">.</span>get_cookie_secure<span class="token punctuation">(</span>app<span class="token punctuation">)</span>
        expires <span class="token operator">=</span> self<span class="token punctuation">.</span>get_expiration_time<span class="token punctuation">(</span>app<span class="token punctuation">,</span> session<span class="token punctuation">)</span>
        val <span class="token operator">=</span> self<span class="token punctuation">.</span>get_signing_serializer<span class="token punctuation">(</span>app<span class="token punctuation">)</span><span class="token punctuation">.</span>dumps<span class="token punctuation">(</span>dict<span class="token punctuation">(</span>session<span class="token punctuation">)</span><span class="token punctuation">)</span>
        response<span class="token punctuation">.</span>set_cookie<span class="token punctuation">(</span>app<span class="token punctuation">.</span>session_cookie_name<span class="token punctuation">,</span> val<span class="token punctuation">,</span>
                            expires<span class="token operator">=</span>expires<span class="token punctuation">,</span>
                            httponly<span class="token operator">=</span>httponly<span class="token punctuation">,</span>
                            domain<span class="token operator">=</span>domain<span class="token punctuation">,</span> path<span class="token operator">=</span>path<span class="token punctuation">,</span> secure<span class="token operator">=</span>secure<span class="token punctuation">)</span>
</code></pre>
<p>这段代码也很容易理解，就是从 <code>app</code> 和 <code>session</code> 变量中获取所有需要的信息，然后调用 <code>response.set_cookie</code> 设置最后的 <code>cookie</code>。这样客户端就能在 cookie 中保存 session 有关的信息，以后访问的时候再次发送给服务器端，以此来实现有状态的交互。</p>
<h2 id="解密-session"><a href="#解密-session" class="headerlink" title="解密 session"></a>解密 session</h2><p>有时候在开发或者调试的过程中，需要了解 cookie 中保存的到底是什么值，可以通过手动解析它的值。<code>session</code> 在 <code>cookie</code> 中的值，是一个字符串，由句号分割成三个部分。第一部分是 <code>base64</code> 加密的数据，第二部分是时间戳，第三部分是校验信息。</p>
<p>前面两部分的内容可以通过下面的方式获取，代码也可直观，就不给出解释了：</p>
<pre class=" language-bash"><code class="language-bash">In <span class="token punctuation">[</span>1<span class="token punctuation">]</span>: from itsdangerous <span class="token function">import</span> *

In <span class="token punctuation">[</span>2<span class="token punctuation">]</span>: s <span class="token operator">=</span> <span class="token string">'eyJ1c2VybmFtZSI6ImNpeml4cyJ9.C5fdpg.fqm3FTv0kYE2TuOyGF1mx2RuYQ4'</span>

In <span class="token punctuation">[</span>3<span class="token punctuation">]</span>: data, timstamp, secret <span class="token operator">=</span> s.split<span class="token punctuation">(</span><span class="token string">'.'</span><span class="token punctuation">)</span>

In <span class="token punctuation">[</span>4<span class="token punctuation">]</span>: base64_decode<span class="token punctuation">(</span>data<span class="token punctuation">)</span>
Out<span class="token punctuation">[</span>4<span class="token punctuation">]</span>: <span class="token string">'{"username":"cizixs"}'</span>

In <span class="token punctuation">[</span>5<span class="token punctuation">]</span>: bytes_to_int<span class="token punctuation">(</span>base64_decode<span class="token punctuation">(</span>timstamp<span class="token punctuation">))</span>
Out<span class="token punctuation">[</span>5<span class="token punctuation">]</span>: 194502054

In <span class="token punctuation">[</span>7<span class="token punctuation">]</span>: time.strftime<span class="token punctuation">(</span><span class="token string">'%Y-%m-%d %H:%I%S'</span>, time.localtime<span class="token punctuation">(</span>194502054+EPOCH<span class="token punctuation">))</span>
Out<span class="token punctuation">[</span>7<span class="token punctuation">]</span>: <span class="token string">'2017-03-01 12:1254'</span>
</code></pre>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>flask 默认提供的 session 功能还是很简单的，满足了基本的功能。但是我们看到 flask 把 session 的数据都保存在客户端的 cookie 中，这里只有用户名还好，如果有一些私密的数据（比如密码，账户余额等等），就会造成严重的安全问题。可以考虑使用 <a href="https://pythonhosted.org/Flask-Session/" target="_blank" rel="noopener">flask-session</a> 这个三方的库，它把数据保存在服务器端（本地文件、redis、memcached），客户端只拿到一个 sessionid。</p>
<p>session 主要是用来在不同的请求之间保存信息，最常见的应用就是登陆功能。虽然直接通过 <code>session</code> 自己也可以写出来不错的登陆功能，但是在实际的项目中可以考虑 <a href="https://flask-login.readthedocs.io/en/latest/" target="_blank" rel="noopener"><code>flask-login</code></a> 这个三方的插件，方便我们的开发</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://github.com/fengsp/flask-session" target="_blank" rel="noopener">flask-session github page</a></li>
<li><a href="http://blog.csdn.net/yueguanghaidao/article/details/40016235" target="_blank" rel="noopener">Flask 源码阅读笔记</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="virtualbox 常用网络模式解释和配置" href="/2017/03/09/virtualbox-network-mode-explained/">
        ← virtualbox 常用网络模式解释和配置
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="linux 网络虚拟化： ipvlan" href="/2017/02/17/network-virtualization-ipvlan/">
        linux 网络虚拟化： ipvlan →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#session-简介"><span class="toc-text">session 简介</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#解析"><span class="toc-text">解析</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#请求过程"><span class="toc-text">请求过程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#session-对象"><span class="toc-text">session 对象</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#签名算法"><span class="toc-text">签名算法</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#应答过程"><span class="toc-text">应答过程</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#解密-session"><span class="toc-text">解密 session</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#总结"><span class="toc-text">总结</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">flask 源码解析：session</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2017/03/08/flask-insight-session/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2017/03/08/flask-insight-session/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
